///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __Camera_H__
#define __Camera_H__

// Default options
#include "OgrePrerequisites.h"

#include "OgreString.h"
#include "OgreMovableObject.h"

// Matrices & Vectors
#include "math/matrix4.h "
#include "math/vector3.h"
#include "math/plane.h"
#include "math/quaternion.h"
#include "OgreCommon.h"
#include "OgreFrustum.h"
#include "math/ray.h"
#include "math/planeBoundedVolume.h"


namespace Ogre {

    class _OgreExport Camera : public Frustum
    {
	public:
		
		class _OgreExport Listener 
		{
		public:
			Listener() {}
			virtual ~Listener() {}

			/// Called prior to the scene being rendered with this camera
			virtual void cameraPreRenderScene(Camera* cam)
                        { (void)cam; }

			/// Called after the scene has been rendered with this camera
			virtual void cameraPostRenderScene(Camera* cam)
                        { (void)cam; }

			/// Called when the camera is being destroyed
			virtual void cameraDestroyed(Camera* cam)
                        { (void)cam; }

		};
    protected:
        /// Scene manager responsible for the scene
        SceneManager *mSceneMgr;

        /// Camera orientation, quaternion style
        Quaternion mOrientation;

        /// Camera position - default (0,0,0)
        Vector3 mPosition;

        /// Derived orientation/position of the camera, including reflection
        mutable Quaternion mDerivedOrientation;
        mutable Vector3 mDerivedPosition;

        /// Real world orientation/position of the camera
        mutable Quaternion mRealOrientation;
        mutable Vector3 mRealPosition;

        /// Whether to yaw around a fixed axis.
        bool mYawFixed;
        /// Fixed axis to yaw around
        Vector3 mYawFixedAxis;

        /// Rendering type
        PolygonMode mSceneDetail;

        /// Stored number of visible faces in the last render
        unsigned int mVisFacesLastRender;

        /// Stored number of visible faces in the last render
        unsigned int mVisBatchesLastRender;

        /// Shared class-level name for Movable type
        static String msMovableType;

        /// SceneNode which this Camera will automatically track
        SceneNode* mAutoTrackTarget;
        /// Tracking offset for fine tuning
        Vector3 mAutoTrackOffset;

		// Scene LOD factor used to adjust overall LOD
		Real mSceneLodFactor;
		/// Inverted scene LOD factor, can be used by Renderables to adjust their LOD
		Real mSceneLodFactorInv;


        
        Real mWLeft, mWTop, mWRight, mWBottom;
        /// Is viewing window used.
        bool mWindowSet;
        /// Windowed viewport clip planes 
        mutable Math::PlaneList mWindowClipPlanes;
        // Was viewing window changed.
        mutable bool mRecalcWindow;
        /// The last viewport to be added using this camera
        Viewport* mLastViewport;
        
        bool mAutoAspectRatio;
		/// Custom culling frustum
		Frustum *mCullFrustum;
		/// Whether or not the rendering distance of objects should take effect for this camera
		bool mUseRenderingDistance;
        /// Camera to use for LOD calculation
        const Camera* mLodCamera;
		
		/// Whether or not the minimum display size of objects should take effect for this camera
		bool mUseMinPixelSize;
		/// @see Camera::getPixelDisplayRatio
		Real mPixelDisplayRatio;

		typedef vector<Listener*>::type ListenerList;
		ListenerList mListeners;

        bool isViewOutOfDate(void) const;

        void invalidateFrustum(void) const;

        void invalidateView(void) const;

        virtual void setWindowImpl(void) const;

		virtual vector<Vector4>::type getRayForwardIntersect(const Vector3& anchor, const Vector3 *dir, Real planeOffset) const;

    public:
        
        Camera( const String& name, SceneManager* sm);
        
        virtual ~Camera();

		virtual void addListener(Listener* l);

		virtual void removeListener(Listener* l);

        SceneManager* getSceneManager(void) const;
        
        void setPolygonMode(PolygonMode sd);

        PolygonMode getPolygonMode(void) const;

        void setPosition(Real x, Real y, Real z);

        void setPosition(const Vector3& vec);

        const Vector3& getPosition(void) const;

        void move(const Vector3& vec);

        void moveRelative(const Vector3& vec);

        void setDirection(Real x, Real y, Real z);

        void setDirection(const Vector3& vec);

        Vector3 getDirection(void) const;

        Vector3 getUp(void) const;

        Vector3 getRight(void) const;

        void lookAt( const Vector3& targetPoint );
        
        void lookAt(Real x, Real y, Real z);

        void roll(const Radian& angle);

        void yaw(const Radian& angle);

        void pitch(const Radian& angle);

        void rotate(const Vector3& axis, const Radian& angle);

        void rotate(const Quaternion& q);

        void setFixedYawAxis( bool useFixed, const Vector3& fixedAxis = Vector3::UNIT_Y );

        const Quaternion& getOrientation(void) const;

        void setOrientation(const Quaternion& q);

        void _renderScene(Viewport *vp, bool includeOverlays);

        _OgreExport friend std::ostream& operator<<(std::ostream& o, const Camera& c);

        void _notifyRenderedFaces(unsigned int numfaces);

        void _notifyRenderedBatches(unsigned int numbatches);

        unsigned int _getNumRenderedFaces(void) const;

        unsigned int _getNumRenderedBatches(void) const;

        const Quaternion& getDerivedOrientation(void) const;
        
        const Vector3& getDerivedPosition(void) const;
        
        Vector3 getDerivedDirection(void) const;
        
        Vector3 getDerivedUp(void) const;
        
        Vector3 getDerivedRight(void) const;

        const Quaternion& getRealOrientation(void) const;
        
        const Vector3& getRealPosition(void) const;
        
        Vector3 getRealDirection(void) const;
        
        Vector3 getRealUp(void) const;
        
        Vector3 getRealRight(void) const;

        void getWorldTransforms(Matrix4* mat) const;

        const String& getMovableType(void) const;

        void setAutoTracking(bool enabled, SceneNode* const target = 0, 
            const Vector3& offset = Vector3::ZERO);

		void setLodBias(Real factor = 1.0);
		
		Real getLodBias(void) const;
		
		virtual void setLodCamera(const Camera* lodCam);

		virtual const Camera* getLodCamera() const;
   
        Ray getCameraToViewportRay(Real screenx, Real screeny) const;
        
        void getCameraToViewportRay(Real screenx, Real screeny, Ray* outRay) const;
		
		PlaneBoundedVolume getCameraToViewportBoxVolume(Real screenLeft, 
			Real screenTop, Real screenRight, Real screenBottom, bool includeFarPlane = false);
		
		void getCameraToViewportBoxVolume(Real screenLeft, 
			Real screenTop, Real screenRight, Real screenBottom, 
			PlaneBoundedVolume* outVolume, bool includeFarPlane = false);
		
		Real _getLodBiasInverse(void) const;
        
        void _autoTrack(void);
        
        virtual void setWindow (Real Left, Real Top, Real Right, Real Bottom);
        /// Cancel view window.
        virtual void resetWindow (void);
        /// Returns if a viewport window is being used
        virtual bool isWindowSet(void) const { return mWindowSet; }
        /// Gets the window clip planes, only applicable if isWindowSet == true
        const Math::PlaneList& getWindowPlanes(void) const;

        Real getBoundingRadius(void) const;
		
        SceneNode* getAutoTrackTarget(void) const { return mAutoTrackTarget; }
		
		const Vector3& getAutoTrackOffset(void) const { return mAutoTrackOffset; }
		
        Viewport* getViewport(void) const {return mLastViewport;}
        
        void _notifyViewport(Viewport* viewport) {mLastViewport = viewport;}

        void setAutoAspectRatio(bool autoratio);

        bool getAutoAspectRatio(void) const;

		void setCullingFrustum(Frustum* frustum) { mCullFrustum = frustum; }
		
		Frustum* getCullingFrustum(void) const { return mCullFrustum; }

		virtual void forwardIntersect(const Plane& worldPlane, vector<Vector4>::type* intersect3d) const;

		/// @copydoc Frustum::isVisible
		bool isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy = 0) const;
		/// @copydoc Frustum::isVisible
		bool isVisible(const Sphere& bound, FrustumPlane* culledBy = 0) const;
		/// @copydoc Frustum::isVisible
		bool isVisible(const Vector3& vert, FrustumPlane* culledBy = 0) const;
		/// @copydoc Frustum::getWorldSpaceCorners
		const Vector3* getWorldSpaceCorners(void) const;
		/// @copydoc Frustum::getFrustumPlane
		const Plane& getFrustumPlane( unsigned short plane ) const;
		/// @copydoc Frustum::projectSphere
		bool projectSphere(const Sphere& sphere, 
			Real* left, Real* top, Real* right, Real* bottom) const;
		/// @copydoc Frustum::getNearClipDistance
		Real getNearClipDistance(void) const;
		/// @copydoc Frustum::getFarClipDistance
		Real getFarClipDistance(void) const;
		/// @copydoc Frustum::getViewMatrix
		const Matrix4& getViewMatrix(void) const;
		
		const Matrix4& getViewMatrix(bool ownFrustumOnly) const;
		
		virtual void setUseRenderingDistance(bool use) { mUseRenderingDistance = use; }
		
		virtual bool getUseRenderingDistance(void) const { return mUseRenderingDistance; }

		virtual void synchroniseBaseSettingsWith(const Camera* cam);

		const Vector3& getPositionForViewUpdate(void) const;
		
		const Quaternion& getOrientationForViewUpdate(void) const;

		void setUseMinPixelSize(bool enable) { mUseMinPixelSize = enable; }
		
		bool getUseMinPixelSize() const { return mUseMinPixelSize; }

		Real getPixelDisplayRatio() const { return mPixelDisplayRatio; }
		
     };

} // namespace Ogre
#endif
